﻿using System;
using Microsoft.SharePoint;

namespace GDExtensions
{
    /// <summary>
    /// Extension methods for the <see cref="T:Microsoft.SharePoint.SPListItem"/> class.
    /// </summary>
    /// <remarks></remarks>
    public static class SPListItemExtensions
    {
        #region Public Methods
        /// <summary>
        /// Method to grant user/group access to the <paramref name="listItem"/>.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="id">The <see cref="T:Microsoft.SharePoint.SPGroup"/> or <see cref="T:Microsoft.SharePoint.SPUser"/> ID.</param>
        /// <param name="roleType">The <see cref="T:Microsoft.SharePoint.SPRoleType"/> role for the <paramref name="listItem"/>.</param>
        /// <remarks></remarks>
        public static void AddSecurityById(this SPListItem listItem, int id, SPRoleType roleType)
        {
            // Ensure the list item exists
            if (listItem != null)
            {
                // Add the security group to the list item
                listItem.AddSecurityById(id, listItem.ParentList.ParentWeb.RoleDefinitions.GetByType(roleType));
            }
        }

        /// <summary>
        /// Method to grant user/group access to the <paramref name="listItem"/>.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="id">The <see cref="T:Microsoft.SharePoint.SPGroup"/> or <see cref="T:Microsoft.SharePoint.SPUser"/> ID.</param>
        /// <param name="roleDefinition">The <see cref="T:Microsoft.SharePoint.SPRoleDefinition"/> role definition for the <paramref name="listItem"/>.</param>
        /// <remarks></remarks>
        public static void AddSecurityById(this SPListItem listItem, int id, SPRoleDefinition roleDefinition)
        {
            // Call the private method
            _AddSecurityById(listItem, id, roleDefinition);
        }

        /// <summary>
        /// Method to add a security group to the <paramref name="listItem"/>.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="group">The <see cref="T:Microsoft.SharePoint.SPGroup"/> group to add.</param>
        /// <param name="roleName">The name of the <see cref="T:Microsoft.SharePoint.SPRoleDefinition"/>.</param>
        /// <remarks></remarks>
        public static void AddSecurityGroup(this SPListItem listItem, SPGroup group, string roleName)
        {
            // Ensure the listItem & group exist
            if (listItem != null && group != null)
            {
                // Add the security group to the folder
                listItem.AddSecurityGroup(group, listItem.ParentList.ParentWeb.GetRoleDefinitionByName(roleName));
            }
        }

        /// <summary>
        /// Method to add a security group to the <paramref name="listItem"/>.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="group">The <see cref="T:Microsoft.SharePoint.SPGroup"/> group to add.</param>
        /// <param name="roleType">The <see cref="T:Microsoft.SharePoint.SPRoleType"/> role for the <paramref name="group"/>.</param>
        /// <remarks></remarks>
        public static void AddSecurityGroup(this SPListItem listItem, SPGroup group, SPRoleType roleType)
        {
            // Ensure the group exists
            if (group != null)
            {
                // Add the security group
                listItem.AddSecurityById(group.ID, listItem.ParentList.ParentWeb.RoleDefinitions.GetByType(roleType));
            }
        }

        /// <summary>
        /// Method to add a security group to the <paramref name="listItem"/>.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="group">The <see cref="T:Microsoft.SharePoint.SPGroup"/> group to add.</param>
        /// <param name="roleDefinition">The <see cref="T:Microsoft.SharePoint.SPRoleDefinition"/> role definition for the <paramref name="group"/>.</param>
        /// <remarks></remarks>
        public static void AddSecurityGroup(this SPListItem listItem, SPGroup group, SPRoleDefinition roleDefinition)
        {
            // Ensure the group exists
            if (group != null)
            {
                // Add the security group
                listItem.AddSecurityById(group.ID, roleDefinition);
            }
        }

        /// <summary>
        /// Method to clear the <paramref name="listItem"/> permissions.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <remarks></remarks>
        public static void ClearPermissions(this SPListItem listItem)
        {
            // Call the private method
            _ClearPermissions(listItem);
        }

        /// <summary>
        /// Method to determine if a value exists for the <paramref name="fieldName"/> in the <paramref name="listItem"/>.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="fieldName">The <see cref="T:Microsoft.SharePoint.SPField"/> field name.</param>
        /// <returns><c>true</c> if <paramref name="fieldName"/> value exists; otherwise, <c>false</c>.</returns>
        /// <remarks></remarks>
        public static bool FieldValueExists(this SPListItem listItem, string fieldName)
        {
            // Ensure the list item exists
            if (listItem != null)
            {
                // See if a field value exists
                return listItem.FieldValueExists(listItem.GetField(fieldName));
            }
            return false;
        }

        /// <summary>
        /// Method to determine if a value exists for the <paramref name="field"/> in the <paramref name="listItem"/>.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="field">The <see cref="T:Microsoft.SharePoint.SPField"/> field.</param>
        /// <returns><c>true</c> if <paramref name="field"/> value exists; otherwise, <c>false</c>.</returns>
        /// <remarks></remarks>
        public static bool FieldValueExists(this SPListItem listItem, SPField field)
        {
            // Call the private method
            return _FieldValueExists(listItem, field);
        }

        /// <summary>
        /// Method to get the <see cref="Microsoft.SharePoint.SPList"/> field by the display or internal name.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="fieldName">The <see cref="T:Microsoft.SharePoint.SPField"/> field name.</param>
        /// <returns>The <see cref="T:Microsoft.SharePoint.SPField"/> field if it exists, else NULL.</returns>
        /// <remarks></remarks>
        public static SPField GetField(this SPListItem listItem, string fieldName)
        {
            // Call the private method
            return _GetField(listItem, fieldName);
        }

        /// <summary>
        /// Method to get the <paramref name="listItem"/> field value.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="fieldName">Display or internal field name</param>
        /// <returns>The <paramref name="listItem"/> field value</returns>
        /// <remarks></remarks>
        public static string GetFieldValue(this SPListItem listItem, string fieldName)
        {
            // Return the field value
            return listItem.GetFieldValue(listItem.GetField(fieldName), false);
        }

        /// <summary>
        /// Method to get the <paramref name="listItem"/> field value.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="fieldName">The <see cref="T:Microsoft.SharePoint.SPField"/> field name.</param>
        /// <param name="rawValue">if set to <c>true</c> returns the raw string value, else uses the GetFieldValueAsText method of the <see cref="T:Microsoft.SharePoint.SPField"/> class.</param>
        /// <returns>The <paramref name="listItem"/> field value.</returns>
        /// <remarks></remarks>
        public static string GetFieldValue(this SPListItem listItem, string fieldName, bool rawValue)
        {
            // Return the field value
            return listItem.GetFieldValue(listItem.GetField(fieldName), rawValue);
        }

        /// <summary>
        /// Method to get the <paramref name="listItem"/> field value.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="field">The <see cref="T:Microsoft.SharePoint.SPField"/> field.</param>
        /// <returns>The <paramref name="listItem"/> field value.</returns>
        /// <remarks></remarks>
        public static string GetFieldValue(this SPListItem listItem, SPField field)
        {
            // Return the field value
            return listItem.GetFieldValue(field, false);
        }

        /// <summary>
        /// Method to get the <paramref name="listItem"/> field value.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="field">The <see cref="T:Microsoft.SharePoint.SPField"/> field.</param>
        /// <param name="rawValue">if set to <c>true</c> returns the raw string value, else uses the GetFieldValueAsText method of the <see cref="T:Microsoft.SharePoint.SPField"/> class.</param>
        /// <returns>The <paramref name="listItem"/> field value.</returns>
        /// <remarks></remarks>
        public static string GetFieldValue(this SPListItem listItem, SPField field, bool rawValue)
        {
            // Call the private method
            return _GetFieldValue(listItem, field, rawValue);
        }

        /// <summary>
        /// Method to remove access to the <paramref name="listItem"/>.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="userId">The ID.</param>
        /// <remarks></remarks>
        public static void RemoveSecurityById(this SPListItem listItem, int userId)
        {
            // Call the private method
            _RemoveSecurityById(listItem, userId);
        }

        /// <summary>
        /// Method to add a security group to the <paramref name="listItem"/>.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="groupName">The <see cref="T:Microsoft.SharePoint.SPGroup"/> field name.</param>
        /// <remarks></remarks>
        public static void RemoveSecurityGroup(this SPListItem listItem, string groupName)
        {
            // Remove the security group
            listItem.RemoveSecurityGroup(listItem.ParentList.ParentWeb.GetSecurityGroup(groupName));
        }

        /// <summary>
        /// Method to add a security group to the <paramref name="listItem"/>.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="group">The <see cref="T:Microsoft.SharePoint.SPGroup"/> group to add.</param>
        /// <remarks></remarks>
        public static void RemoveSecurityGroup(this SPListItem listItem, SPGroup group)
        {
            // Ensure the group exists
            if (group != null)
            {
                // Remove the security group
                listItem.RemoveSecurityById(group.ID);
            }
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Method to grant user/group access to the <paramref name="listItem"/>.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="id">The <see cref="T:Microsoft.SharePoint.SPGroup"/> or <see cref="T:Microsoft.SharePoint.SPUser"/> ID.</param>
        /// <param name="roleDefinition">The <see cref="T:Microsoft.SharePoint.SPRoleDefinition"/> role definition for the <paramref name="listItem"/>.</param>
        /// <remarks></remarks>
        private static void _AddSecurityById(SPListItem listItem, int id, SPRoleDefinition roleDefinition)
        {
            SPUser user = default(SPUser);
            SPGroup group = default(SPGroup);

            // Ensure the list item & role definition exist
            if (listItem == null || roleDefinition == null)
            {
                return;
            }

            // Try to get the user
            try
            {
                user = listItem.ParentList.ParentWeb.AllUsers.GetByID(id);
            }
            // Else, this is a group
            catch
            {
                try
                {
                    group = listItem.ParentList.ParentWeb.SiteGroups.GetByID(id);
                }
                // Error getting the user/group by id
                catch { return; }
            }

            // Create the role assignment
            SPPrincipal principal = user == null ? group as SPPrincipal : user as SPPrincipal;
            SPRoleAssignment assignment = new SPRoleAssignment(principal);

            // Add the role definition
            assignment.RoleDefinitionBindings.Add(roleDefinition);

            // Ensure the folder has unique role assignments
            if (!listItem.HasUniqueRoleAssignments)
            {
                // Break inheritance
                listItem.BreakRoleInheritance(true);
            }

            // Add the group to the list item
            listItem.RoleAssignments.Add(assignment);
        }

        /// <summary>
        /// Method to clear the <paramref name="listItem"/> permissions.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <remarks></remarks>
        private static void _ClearPermissions(SPListItem listItem)
        {
            // Ensure the list item exists
            if (listItem == null) { return; }

            // Ensure the list item contains role assignments
            if (listItem.RoleAssignments.Count < 1) { return; }

            // Break the role inheritance
            listItem.BreakRoleInheritance(false);

            // Parse the role assignments
            for (int i = listItem.RoleAssignments.Count - 1; i >= 0; i--)
            {
                // Clear the current role assignement
                listItem.RoleAssignments.Remove(i);
            }

            // Get the associated owners group
            SPGroup ownersGroup = listItem.ParentList.ParentWeb.GetAssociatedSecurityGroup(AssociatedSecurityGroups.Owners);
            if (ownersGroup != null)
            {
                // Add the owners group by default, and give it full control
                listItem.AddSecurityGroup(ownersGroup, SPRoleType.Administrator);
            }
        }

        /// <summary>
        /// Method to determine if a value exists for the <paramref name="field"/> in the <paramref name="listItem"/>.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="field">The <see cref="T:Microsoft.SharePoint.SPField"/> field.</param>
        /// <returns><c>true</c> if <paramref name="field"/> value exists; otherwise, <c>false</c>.</returns>
        /// <remarks></remarks>
        private static bool _FieldValueExists(SPListItem listItem, SPField field)
        {
            // Ensure the list item & field exists
            if (listItem != null && field != null)
            {
                // Determine if a field value exists
                return listItem[field.Id] != null;
            }
            return false;
        }

        /// <summary>
        /// Method to get the <see cref="Microsoft.SharePoint.SPList"/> field by the display or internal name.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="fieldName">The <see cref="T:Microsoft.SharePoint.SPField"/> field name.</param>
        /// <returns>The <see cref="T:Microsoft.SharePoint.SPField"/> field if it exists, else NULL.</returns>
        /// <remarks></remarks>
        private static SPField _GetField(SPListItem listItem, string fieldName)
        {
            SPField field = default(SPField);

            // Ensure the list item exists
            if (listItem != null)
            {
                // Get the field
                field = listItem.ParentList.GetField(fieldName);
            }

            // Return the field
            return field;
        }

        /// <summary>
        /// Method to get the <paramref name="listItem"/> field value.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="field">The <see cref="T:Microsoft.SharePoint.SPField"/> field.</param>
        /// <param name="rawValue">if set to <c>true</c> returns the raw string value, else uses the GetFieldValueAsText method of the <see cref="T:Microsoft.SharePoint.SPField"/> class.</param>
        /// <returns>The <paramref name="listItem"/> field value.</returns>
        /// <remarks></remarks>
        private static string _GetFieldValue(SPListItem listItem, SPField field, bool rawValue)
        {
            string fieldValue = string.Empty;

            // Ensure the field exists
            if (field != null)
            {
                // Set the field value
                fieldValue = rawValue ?
                    listItem[field.Id] + "" : field.GetFieldValueAsText(listItem[field.Id]);
            }

            // Return the field value
            return fieldValue;
        }

        /// <summary>
        /// Method to remove user access to the <paramref name="listItem"/>.
        /// </summary>
        /// <param name="listItem">The <see cref="T:Microsoft.SharePoint.SPListItem"/> item.</param>
        /// <param name="id">The <see cref="T:Microsoft.SharePoint.SPGroup"/> or <see cref="T:Microsoft.SharePoint.SPUser"/> ID.</param>
        /// <remarks></remarks>
        private static void _RemoveSecurityById(SPListItem listItem, int id)
        {
            // Ensure the list item exists
            if (listItem != null)
            {
                // Ensure the list item is not inheriting permissions
                if (!listItem.HasUniqueRoleAssignments)
                {
                    // Break the inheritance
                    listItem.BreakRoleInheritance(true);
                }

                // Remove the security group or user from the list item
                listItem.RoleAssignments.RemoveById(id);
            }
        }
        #endregion
    }
}
